/*
 * Copyright (c) 2016 MariaDB Corporation Ab
 * Copyright (c) 2023 MariaDB plc, Finnish Branch
 *
 * Use of this software is governed by the Business Source License included
 * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 *
 * Change Date: 2027-04-10
 *
 * On the date above, in accordance with the Business Source License, use
 * of this software will be governed by version 2 or later of the General
 * Public License.
 */

#include <maxscale/ccdefs.hh>
#include <chrono>
#include <cstdio>
#include <getopt.h>
#include <unistd.h>

#include <maxbase/log.hh>
#include <maxscale/paths.hh>
#include "internal/secrets.hh"

using std::string;
using ByteVec = std::vector<uint8_t>;

struct option options[] =
{
    {"help",  no_argument,       nullptr, 'h'},
    {"user",  required_argument, nullptr, 'u'},
    {nullptr, 0,                 nullptr, 0  }
};

const string default_user = "maxscale";

void print_usage(const char* executable, const char* default_directory)
{
    const char msg[] =
        R"(usage: %s [-h|--help] [directory]

This utility generates a random AES encryption key and writes it to disk.
The key is written to the file '%s', in the specified directory. Use
'maxpasswd' to encrypt passwords with the generated key. The encrypted
passwords can be used in MaxScale configuration files. MaxScale uses the
key to decrypt the passwords while running.

Re-creating the file invalidates all existing encrypted passwords in the
configuration files.

  -h, --help    Display this help
  -u, --user    Designate the owner of the generated file (default: '%s')

  directory  : The directory where to store the file in (default: '%s')
)";
    printf(msg, executable, SECRETS_FILENAME, default_user.c_str(), default_directory);
}

int main(int argc, char** argv)
{
    mxb::Log log(MXB_LOG_TARGET_STDOUT);
    const string default_directory = mxs::datadir();
    string username = default_user;

    int c;
    while ((c = getopt_long(argc, argv, "hu:", options, nullptr)) != -1)
    {
        switch (c)
        {
        case 'h':
            print_usage(argv[0], default_directory.c_str());
            return EXIT_SUCCESS;

        case 'u':
            username = optarg;
            break;

        default:
            print_usage(argv[0], default_directory.c_str());
            return EXIT_FAILURE;
        }
    }

    string filepath = default_directory;
    if (optind < argc)
    {
        filepath = argv[optind];
    }
    filepath.append("/").append(SECRETS_FILENAME);

    // Check that the file doesn't exist.
    errno = 0;
    auto filepathc = filepath.c_str();
    if (access(filepathc, F_OK) == 0)
    {
        printf("Secrets file '%s' already exists. Delete it before generating a new encryption key.\n",
               filepathc);
        return EXIT_FAILURE;
    }
    else if (errno != ENOENT)
    {
        printf("stat() for secrets file '%s' failed unexpectedly. Error %i, %s.\n",
               filepathc, errno, mxb_strerror(errno));
        return EXIT_FAILURE;
    }

    int rval = EXIT_FAILURE;
    auto new_key = mxb::Cipher(SECRETS_CIPHER_MODE, SECRETS_CIPHER_BITS).new_key();
    if (!new_key.empty() && secrets_write_keys(new_key, filepath, username))
    {
        rval = EXIT_SUCCESS;
    }
    return rval;
}
